#include <string.h>
#include <stdio.h>

#include "check.h"
#include "security.h"
#include "security_error.h"

#define FILE_SRC      "/tmp/src.txt"
#define FILE_SRC2     "/tmp/src2.txt"
#define FILE_ENCRYPT  "/tmp/encrypt.txt"
#define FILE_ENCRYPT2 "/tmp/encrypt2.txt"
#define FILE_DECRYPT  "/tmp/decrypt.txt"
#define FILE_DECRYPT2 "/tmp/decrypt2.txt"

START_TEST(des_ok) {
    int ret = 0, i;
    char src1[] = "123";
    char src2[] = "12345678";
    char key[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};

    char ans1[] = {0x2C, 0x38, 0x85, 0x51, 0xD7, 0xF4, 0x89, 0xEC};
    char ans2[] = {0x96, 0xD0, 0x02, 0x88, 0x78, 0xD5, 0x8C, 0x89,
                   0xFE, 0xB9, 0x59, 0xB7, 0xD4, 0x64, 0x2F, 0xCB};

    char en[128] = { 0 };
    size_t en_len = 0;

    char de[128] = { 0 };
    size_t de_len = 0;

    ret = security_des_encrypt(en, &en_len, sizeof(en), src1, strlen(src1), key);
    ck_assert_int_eq(ret, SECURITY_OK);
    ck_assert_int_eq(en_len, sizeof(ans1));
    ck_assert_mem_eq(en, ans1, en_len);

    ret = security_des_decrypt(de, &de_len, sizeof(de), en, en_len, key);
    ck_assert_int_eq(ret, SECURITY_OK);
    ck_assert_int_eq(de_len, strlen(src1));
    ck_assert_mem_eq(de, src1, de_len);

    ret = security_des_encrypt(en, &en_len, sizeof(en), src2, strlen(src2), key);
    ck_assert_int_eq(ret, SECURITY_OK);
    ck_assert_int_eq(en_len, sizeof(ans2));
    ck_assert_mem_eq(en, ans2, en_len);

    ret = security_des_decrypt(de, &de_len, sizeof(de), en, en_len, key);
    ck_assert_int_eq(ret, SECURITY_OK);
    ck_assert_int_eq(de_len, strlen(src2));
    ck_assert_mem_eq(de, src2, de_len);
}
END_TEST

START_TEST(des_input) {
    char out[128] = { 0 };
    char in[] = "123";
    char key[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
    char inp[128] = { 0 };
    size_t olen = 0;


    ck_assert_int_eq(security_des_encrypt(NULL, &olen, sizeof(out), in, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_encrypt(out, NULL, sizeof(out), in, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_encrypt(out, &olen, 0, in, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_encrypt(out, &olen, sizeof(out), NULL, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_encrypt(out, &olen, sizeof(out), in, 0, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_encrypt(out, &olen, sizeof(out), in, sizeof(in), NULL), SECURITY_ERR_BAD_INPUT);

    ck_assert_int_eq(security_des_encrypt(out, &olen, 1, in, 10, key), SECURITY_ERR_BUFFER_TOO_SMALL);


    ck_assert_int_eq(security_des_decrypt(NULL, &olen, sizeof(out), in, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_decrypt(out, NULL, sizeof(out), in, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_decrypt(out, &olen, 0, in, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_decrypt(out, &olen, sizeof(out), NULL, sizeof(in), key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_decrypt(out, &olen, sizeof(out), in, 0, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_decrypt(out, &olen, sizeof(out), in, sizeof(in), NULL), SECURITY_ERR_BAD_INPUT);

    ck_assert_int_eq(security_des_decrypt(out, &olen, sizeof(out), in, 10, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_decrypt(out, &olen, 1, in, 16, key), SECURITY_ERR_BUFFER_TOO_SMALL);
}
END_TEST

START_TEST(des_file_ok) {
    int ret = 0, i;
    char src1[] = "123";
    char src2[] = "12345678";
    char key[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};

    char ans1[] = {0x2C, 0x38, 0x85, 0x51, 0xD7, 0xF4, 0x89, 0xEC};
    char ans2[] = {0x96, 0xD0, 0x02, 0x88, 0x78, 0xD5, 0x8C, 0x89,
                   0xFE, 0xB9, 0x59, 0xB7, 0xD4, 0x64, 0x2F, 0xCB};

    char en[128] = { 0 };
    size_t en_len = 0;

    char de[128] = { 0 };
    size_t de_len = 0;
    FILE *fp = NULL;

    char buffer[128] = { 0 };
    size_t buflen = 0;

    char command[256] = { 0 };
    snprintf(command, sizeof(command), "rm -rf %s %s %s %s %s %s", FILE_SRC, FILE_SRC2, FILE_ENCRYPT, FILE_ENCRYPT2, FILE_DECRYPT, FILE_DECRYPT2);

    system(command);

    fp = fopen(FILE_SRC, "w");
    ck_assert_ptr_ne(fp, NULL);
    fwrite(src1, sizeof(char), strlen(src1), fp);
    fclose(fp);

    fp = fopen(FILE_SRC2, "w");
    ck_assert_ptr_ne(fp, NULL);
    fwrite(src2, sizeof(char), strlen(src2), fp);
    fclose(fp);

    // ==================第一组验证==================

    // 加密/tmp/src.txt到/tmp/encrypt.txt
    ret = security_des_file_encrypt(FILE_SRC, FILE_ENCRYPT, key);
    ck_assert_int_eq(ret, SECURITY_OK);

    // 验证加密结果
    fp = fopen(FILE_ENCRYPT, "r");
    ck_assert_ptr_ne(fp, NULL);
    buflen = fread(buffer, sizeof(char), sizeof(buffer), fp);
    ck_assert_int_eq(buflen, sizeof(ans1));
    ck_assert_mem_eq(buffer, ans1, sizeof(ans1));
    fclose(fp);

    // 解密/tmp/encrypt.txt到/tmp/decrypt.txt
    ret = security_des_file_decrypt(FILE_ENCRYPT, FILE_DECRYPT, key);
    ck_assert_int_eq(ret, SECURITY_OK);

    // 验证解密结果
    fp = fopen(FILE_DECRYPT, "r");
    ck_assert_ptr_ne(fp, NULL);
    buflen = fread(buffer, sizeof(char), sizeof(buffer), fp);
    ck_assert_int_eq(buflen, strlen(src1));
    ck_assert_mem_eq(buffer, src1, strlen(src1));
    fclose(fp);

    // ==================第二组验证==================

    // 加密/tmp/src2.txt到/tmp/encrypt2.txt
    ret = security_des_file_encrypt(FILE_SRC2, FILE_ENCRYPT2, key);
    ck_assert_int_eq(ret, SECURITY_OK);

    // 验证加密结果
    fp = fopen(FILE_ENCRYPT2, "r");
    ck_assert_ptr_ne(fp, NULL);
    buflen = fread(buffer, sizeof(char), sizeof(buffer), fp);
    ck_assert_int_eq(buflen, sizeof(ans2));
    ck_assert_mem_eq(buffer, ans2, sizeof(ans2));
    fclose(fp);

    // 解密/tmp/encrypt2.txt到/tmp/decrypt2.txt
    ret = security_des_file_decrypt(FILE_ENCRYPT2, FILE_DECRYPT2, key);
    ck_assert_int_eq(ret, SECURITY_OK);

    // 验证解密结果
    fp = fopen(FILE_DECRYPT2, "r");
    ck_assert_ptr_ne(fp, NULL);
    buflen = fread(buffer, sizeof(char), sizeof(buffer), fp);
    ck_assert_int_eq(buflen, strlen(src2));
    ck_assert_mem_eq(buffer, src2, strlen(src2));
    fclose(fp);

}
END_TEST;

START_TEST(des_file_input) {
    char key[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};

    // ===================输入测试===================
    ck_assert_int_eq(security_des_file_encrypt(NULL, FILE_ENCRYPT, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_file_encrypt(FILE_SRC, NULL, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_file_encrypt(FILE_SRC, FILE_ENCRYPT, NULL), SECURITY_ERR_BAD_INPUT);

    ck_assert_int_eq(security_des_file_decrypt(NULL, FILE_DECRYPT, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_file_decrypt(FILE_ENCRYPT, NULL, key), SECURITY_ERR_BAD_INPUT);
    ck_assert_int_eq(security_des_file_decrypt(FILE_ENCRYPT, FILE_DECRYPT, NULL), SECURITY_ERR_BAD_INPUT);

    // ===================文件不存在测试===================
    ck_assert_int_eq(security_des_file_encrypt("/tmp/readnone.txttxt", FILE_ENCRYPT, key), SECURITY_ERR_FILE_CANNOT_READ);
    ck_assert_int_eq(security_des_file_encrypt(FILE_SRC, "/tmp/writenone.txttxt", key), SECURITY_OK);

    ck_assert_int_eq(security_des_file_decrypt("/tmp/readnone.txttxt", FILE_DECRYPT, key), SECURITY_ERR_FILE_CANNOT_READ);
    ck_assert_int_eq(security_des_file_decrypt(FILE_ENCRYPT, "/tmp/writenone.txttxt", key), SECURITY_OK);

    // ===================文件没有权限测试===================
    ck_assert_int_eq(security_des_file_encrypt("/etc/shadow", FILE_ENCRYPT, key), SECURITY_ERR_FILE_CANNOT_READ);
    ck_assert_int_eq(security_des_file_encrypt(FILE_SRC, "/root/test.txt", key), SECURITY_ERR_FILE_OPEN);

    // ===================key不匹配测试===================
    ck_assert_int_eq(security_des_file_decrypt(FILE_ENCRYPT, FILE_DECRYPT, "87654321"), SECURITY_DES_DECRYPT_ERROR);
}
END_TEST

Suite *check_des(void)
{
    Suite *s;
    TCase *tc_core;

    s = suite_create("des test");

    tc_core = tcase_create("core");
    tcase_add_test(tc_core, des_ok);
    tcase_add_test(tc_core, des_input);
    tcase_add_test(tc_core, des_file_ok);
    tcase_add_test(tc_core, des_file_input);

    suite_add_tcase(s, tc_core);

    return s;
}